#include "CapDrvDlg.h"
#include "resource.h"

#include "CapVideo.h"
#include <tchar.h>
#include "convert.h"
#include <crtdbg.h>

#include "../libX264Encoder/encoder.h"

#include <stdio.h>

extern "C"
{
#include "../libH264Decoder/libH264Decoder.h"
#include "../libH264RTPSender/h264RtpSender.h"
}

#pragma comment(lib, "..\\lib\\libH264Decoder.lib")
#pragma comment(lib, "..\\lib\\libX264Encoder.lib")
#pragma comment(lib, "wsock32.lib")

extern HWND hwndSelCapDrvDlg_LBox;
extern HWND hwndSelCapDrvDlg;
extern HWND hwndVideo;

extern CTCPSender spsPpsSender;
extern CH264RTPSender rtpSender;

CX264VideoEncoder encoder;

extern FILE* file;

/*
 * some parameter of the capture driver
 */
int gPicWidth;
int gPicHeight;
int gPicBitCount;
int gPicCompression;

/*
 * the total number of pixels
 */
int gPicSize;

LRESULT CALLBACK SelCapDrvProc( HWND hWnd, UINT msg, 
	WPARAM wParam, LPARAM lParam )
{
	int sel = 0;
	switch(msg)
	{
	case WM_INITDIALOG:
		return TRUE;
	case WM_COMMAND:
		switch ( wParam )
		{
		case SELCAPDRVDLG_BUTTON:
			SendMessage( hwndSelCapDrvDlg_LBox, LB_GETSELITEMS, 1, (LPARAM)&sel);
			SendMessage( hwndSelCapDrvDlg, WM_CLOSE, 0, 0);

			CAPTUREPARMS capturepar;
			capCaptureGetSetup(hwndVideo, &capturepar, sizeof(CAPTUREPARMS));

			// set the capturing frame rate
			capturepar.dwRequestMicroSecPerFrame = MICRO_SEC_PER_FRAME; 
			capturepar.fYield = TRUE;

			// do not capturing audio
			capturepar.fCaptureAudio = FALSE;
			capturepar.fAbortLeftMouse = FALSE;
			capturepar.fAbortRightMouse = FALSE;
			capCaptureSetSetup(hwndVideo, &capturepar, sizeof(CAPTUREPARMS));

			// keep connecting to capture driver until connecting successfully
			while(!capDriverConnect(hwndVideo, sel)); 

			/*
			 * display the format of video
			 */
			BITMAPINFO bitmapInfo;
			capGetVideoFormat(hwndVideo, &bitmapInfo, sizeof(BITMAPINFOHEADER));

			TCHAR videoFormat[200];
			_stprintf(videoFormat, 
				TEXT("video width: %d\nvideo height: %d\n \
					 bitcount: %d\nvideo compression: %d\n"),
				bitmapInfo.bmiHeader.biWidth,
				bitmapInfo.bmiHeader.biHeight,
				bitmapInfo.bmiHeader.biBitCount,
				bitmapInfo.bmiHeader.biCompression);

			gPicWidth = bitmapInfo.bmiHeader.biWidth;
			gPicHeight = bitmapInfo.bmiHeader.biHeight;
			gPicBitCount = bitmapInfo.bmiHeader.biBitCount;
			gPicCompression = bitmapInfo.bmiHeader.biCompression;

			gPicSize = gPicWidth*gPicHeight;

			//MessageBox(NULL, videoFormat, videoFormat, NULL);

			// no preview
// 			capPreview(hwndVideo, TRUE);
// 
// 			CAPDRIVERCAPS CapDrvCaps; 
// 			capDriverGetCaps(hwndVideo, &CapDrvCaps, sizeof(CAPDRIVERCAPS)); 
// 			if (CapDrvCaps.fHasOverlay) 
// 				capOverlay(hwndVideo, TRUE);

			// set the callback function of video stream
			capSetCallbackOnVideoStream(hwndVideo, &capVideoStreamCallback);

			// start capturing
			capCaptureSequenceNoFile(hwndVideo);
		}
		return TRUE;
	case WM_CLOSE:
		DestroyWindow(hwndSelCapDrvDlg);
		return TRUE;
	}

	return( 0L );
}

int EnumCapDrv()
{
	TCHAR szDeviceName[80];
	TCHAR szDeviceVersion[80];
	TCHAR item[161];
	int i;

	for (i=0; i<10; i++)
	{
		if ( capGetDriverDescription(i, szDeviceName, sizeof(szDeviceName),
			szDeviceVersion, sizeof(szDeviceVersion)) )
		{
			_tcscpy(item, szDeviceName);
			_tcscat(item, TEXT(" "));
			_tcscat(item, szDeviceVersion);
			SendMessage(hwndSelCapDrvDlg_LBox, LB_ADDSTRING, 0, 
				(LPARAM) item);
			SendMessage(hwndSelCapDrvDlg_LBox, LB_SETITEMDATA, i, (LPARAM) i);
		}
	}

	return 0;
}

LRESULT CALLBACK capVideoStreamCallback(HWND hWnd, LPVIDEOHDR lpVHdr)
{
	// the format of capture video if 'YUY2'
	if ( 0x32595559 == gPicCompression )
	{
		encode_YUY2(lpVHdr->lpData);
	}
	// uncompressed RGB24 (BGR sequence)
	else if ( 0 == gPicCompression && 24 == gPicBitCount)
	{
		encode_RGB24(lpVHdr->lpData);
	}

	return 0;
}

/*
 * the format of video captured by the current device is
 * YUY2. Firstly, convert the YUY2 to RGB24 (RGB sequence)
 */
void encode_YUY2( unsigned char* yuy2Buf ) 
{
	unsigned char* RGBBuf = (unsigned char*)malloc(gPicSize*3);

	YUY2_To_RGB24(yuy2Buf, RGBBuf, gPicSize<<1);

	/*
	 * here, if saving the BGR sequence buffer(not RGB sequence,
	 * when we write RGB information to DIB file, we are writing
	 * BGR sequence in fact from the bottom to top of the picture), 
	 * we will get one .bmp file turning upside down
	 */
	//savePic(BGR, 640, 480);

	unsigned char* convertedRGB = NULL;
	bool converted = false;

	/*
	 * if needed, 
	 * convert the current resolution of picture to standard resolution
	 */
	if ( WIDTH != gPicWidth || HEIGHT != gPicHeight )
	{
		converted = true;
		convertedRGB = (unsigned char*)malloc(WIDTH*HEIGHT*3);
		ConvertPic(RGBBuf, convertedRGB, gPicWidth, gPicHeight, WIDTH, HEIGHT);
	}	

	/*
	 * convert RGB24 to standard YUV format: I420
	 */
	unsigned char* I420Buf = NULL;
	I420Buf = (unsigned char*)malloc(WIDTH*HEIGHT<<1);
	RGB24_To_I420(converted ? convertedRGB : RGBBuf, I420Buf, WIDTH, HEIGHT);

	/*
	 * encode I420 to H264 and send it using RTP
	 */
	int h264BufLen = (gPicSize<<1);
	unsigned char* h264Buf = (unsigned char*)malloc(h264BufLen);
	I420_2_H264(I420Buf, h264Buf, h264BufLen);

	//fwrite(h264Buf, h264BufLen, 1, file);

 	// use the RTP to send the Nal
 	rtpSender.sendPacket(h264Buf, h264BufLen);

	/*
	 * free the memory
	 */
	if (RGBBuf) free(RGBBuf);
	if (convertedRGB) free(convertedRGB);
	if (I420Buf) free(I420Buf);
	if (h264Buf) free(h264Buf);
}

/*
 * the format of video captured by the current device is 
 * uncompressed RGB24, that is, BGR sequence
 */
void encode_RGB24( unsigned char* BGRBuf )
{
	/*
	 * here, if saving the BGR buffer to one .bmp file,
	 * we will get one normal picture
	 */
	//savePic(BGRBuf, 640, 400);

	unsigned char* convertedBGR = NULL;
	bool converted = false;

	/*
	 * if needed, 
	 * convert the current resolution of picture to standard resolution
	 */
	if ( WIDTH != gPicWidth || HEIGHT != gPicHeight )
	{
		converted = true;
		convertedBGR = (unsigned char*)malloc(WIDTH*HEIGHT*3);
		ConvertPic(BGRBuf, convertedBGR, gPicWidth, gPicHeight, WIDTH, HEIGHT);
	}

	/*
	 * convert RGB24 to standard YUV format: I420
	 */
	unsigned char* I420Buf = NULL;
	I420Buf = (unsigned char*)malloc(WIDTH*HEIGHT<<1);
	BGR24_To_I420(converted ? convertedBGR : BGRBuf, I420Buf, WIDTH, HEIGHT);

	/*
	 * encode I420 to H264 and send it using RTP
	 */
	int h264BufLen = gPicSize;
	unsigned char* h264Buf = (unsigned char*)malloc(h264BufLen);
	I420_2_H264(I420Buf, h264Buf, h264BufLen);

/*	fwrite(h264Buf, h264BufLen, 1, file);*/

 	// use the RTP to send the Nal
 	rtpSender.sendPacket(h264Buf, h264BufLen);

	if (convertedBGR) free(convertedBGR);
	if (I420Buf) free(I420Buf);
	if (h264Buf) free(h264Buf);
}

void I420_2_H264(IN unsigned char* I420Buf, OUT unsigned char* h264Buf, IN OUT int& h264BufLen)
{
	static int encoderOpen = 0;
	if (!encoderOpen)
	{
		int width = WIDTH;
		int height = HEIGHT;
		int bAnnexB = 1;
		uint8_t sps[64];
		uint8_t pps[64];
		int spsLen = 0;
		int ppsLen = 0;

		if (0==(encoderOpen=encoder.Open(width, height, bAnnexB, sps, &spsLen, pps, &ppsLen)))
		{
			fprintf(stdout, "Failed to open encoder!\n");
			return;
		}

		/*
		 * use TCP to send SPS & PPS
		 */
		byte btNalLen[2];
		btNalLen[0] = (byte)(spsLen/256);
		btNalLen[1] = (byte)(spsLen%256);

 		spsPpsSender.TCPSend(btNalLen, 2);
 		spsPpsSender.TCPSend(sps, spsLen);
		//fwrite(sps, spsLen, 1, file);
		//fwrite(pps, ppsLen, 1, file);

		btNalLen[0] = (byte)(ppsLen/256);
		btNalLen[1] = (byte)(ppsLen%256);

 		spsPpsSender.TCPSend(btNalLen, 2);
 		spsPpsSender.TCPSend(pps, ppsLen);

		Sleep(2000);
	}

	/*
	 * encode I420 to H264
	 */
	encode(I420Buf, h264Buf, h264BufLen, encoder);
}

/*
 * if saving BGR buffer to one .bmp file,
 * BGR[0] is corresponding to the B of the left-bottom coordinate(0, height-1)
 * BGR[1] is corresponding to the G of the left-bottom coordinate(0, height-1)
 * BGR[2] is corresponding to the R of the left-bottom coordinate(0, height-1)
 * BGR[3] is corresponding to the B of (1, height-1)
 * BGR[4] is corresponding to the G of (1, height-1)
 * BGR[5] is corresponding to the R of (1, height-1)
 *  .
 *  .
 *  .
 * ...........................the B of (0, height-2)
 * ...........................the G of (0, height-2)
 * ...........................the R of (0, height-2)
 * ...........................the B of (1, height-2)
 * ...........................the G of (1, height-2)
 * ...........................the R of (1, height-2)
 *  .
 *  .
 *  .
 * ...........................the B of (0, 0)
 * ...........................the G of (0, 0)
 * ...........................the R of (0, 0)
 * ...........................the B of (1, 0)
 * ...........................the G of (1, 0)
 * ...........................the R of (1, 0)
 *  .
 *  .
 *  .
 * ...........................the B of (width-1, 0)
 * ...........................the G of (width-1, 0)
 * ...........................the R of (width-1, 0)
 */
void savePic(unsigned char* BGR, int width, int height)
{
	static int a = 0;
	a++;
	char filename[26];
	sprintf(filename, "file%d.bmp", a);
	FILE* fp = fopen(filename, "wb");
	BITMAPINFOHEADER bih;
	bih.biBitCount=24;
	bih.biClrImportant=0;
	bih.biClrUsed=0;
	bih.biCompression=0;
	bih.biHeight=height;
	bih.biPlanes=1;
	bih.biSize=sizeof(BITMAPINFOHEADER);
	bih.biSizeImage=width*height*3;
	bih.biWidth=width;
	bih.biXPelsPerMeter=0;
	bih.biYPelsPerMeter=0;

	BITMAPFILEHEADER bfh;
	bfh.bfReserved1=bfh.bfReserved2=0;
	bfh.bfType=0x4d42; //BM
	bfh.bfSize=54+width*height*3;
	bfh.bfOffBits=54;

	fwrite(&bfh,sizeof(BITMAPFILEHEADER), 1, fp);
	fwrite(&bih,sizeof(BITMAPINFOHEADER), 1, fp);
	fwrite(BGR, width*height*3, 1, fp);
	fclose(fp);
}